
#import "CalendarView.h"

@interface CalendarView()

// Gregorian calendar
@property (nonatomic, strong) NSCalendar *calendar;

// Selected day
@property (nonatomic, strong) NSDate * selectedDate;

// Width in point of a day button
@property (nonatomic, assign) NSInteger dayWidth;

// NSCalendarUnit for day, month, year and era.
@property (nonatomic, assign) NSCalendarUnit dayInfoUnits;

// Array of label of weekdays
@property (nonatomic, strong) NSArray * weekDayNames;

// View shake
@property (nonatomic, assign) NSInteger shakes;
@property (nonatomic, assign) NSInteger shakeDirection;

// Gesture recognizers
@property (nonatomic, strong) UISwipeGestureRecognizer * swipeleft;
@property (nonatomic, strong) UISwipeGestureRecognizer * swipeRight;

@property (nonatomic, strong) NSDateComponents *currentShowComps;
@property (nonatomic, strong) NSDateComponents *selectedComps;

@end
@implementation CalendarView

#pragma mark - Init methods

- (instancetype)initCalendarViewWithDidSelectedDate:(NSDate *)didSelectedDate minDate:(NSDate *)minDate maxDate:(NSDate *)maxDate andFrame:(CGRect)frame{
    self = [super initWithFrame:frame];
    if (self){
        _didSelectedDate = didSelectedDate?didSelectedDate:[NSDate date];
        _minDate = minDate;
        _maxDate = maxDate;
        _dayWidth = frame.size.width/8;
        _originX = (frame.size.width - 7*_dayWidth)/2;
        _calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
        _borderWidth = 1;
        _originY = _dayWidth;
        
        _dayInfoUnits = NSEraCalendarUnit | NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit;
        
        _monthAndDayTextColor = BorderColor_Gray;
        _dayBgColorWithoutData = [UIColor whiteColor];
        _dayBgColorWithData = [UIColor whiteColor];
        _dayBgColorSelected = TitleColor;
        
        _dayTxtColorWithoutData = [UIColor blackColor];;
        _dayTxtColorWithData = [UIColor blackColor];
        _dayTxtColorSelected = [UIColor whiteColor];
        
        _borderColor = TitleColor;
        _allowsChangeMonthByDayTap = YES;
        _allowsChangeMonthByButtons = YES;
        _allowsChangeMonthBySwipe = YES;
        _hideMonthLabel = NO;
        _keepSelDayWhenMonthChange = YES;
        
        _nextMonthAnimation = UIViewAnimationOptionTransitionCrossDissolve;
        _prevMonthAnimation = UIViewAnimationOptionTransitionCrossDissolve;
        
        _defaultFont = [UIFont fontWithName:@"HelveticaNeue" size:14.0f];
        _titleFont = [UIFont fontWithName:@"Helvetica-Bold" size:16.0f];
        _weekdayFont = [UIFont fontWithName:@"HelveticaNeue" size:14.0f];
        
        _swipeleft = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(showNextMonth)];
        _swipeleft.direction = UISwipeGestureRecognizerDirectionLeft;
        [self addGestureRecognizer:_swipeleft];
        _swipeRight = [[UISwipeGestureRecognizer alloc]initWithTarget:self action:@selector(showPreviousMonth)];
        _swipeRight.direction = UISwipeGestureRecognizerDirectionRight;
        [self addGestureRecognizer:_swipeRight];
        
        _currentShowComps = [_calendar components:_dayInfoUnits fromDate:_didSelectedDate];
        _selectedComps = [_calendar components:_dayInfoUnits fromDate:_didSelectedDate];
        _currentShowComps.hour = 0;
        _currentShowComps.minute = 0;
        _currentShowComps.second = 0;
        _selectedDate = [_calendar dateFromComponents:_currentShowComps];
        _weekDayNames  = @[@"日", @"一", @"二", @"三",
                           @"四", @"五", @"六"];
        self.backgroundColor = [UIColor clearColor];
    }
    return self;
}

#pragma mark - Custom setters

-(void)setAllowsChangeMonthByButtons:(BOOL)allows
{
    _allowsChangeMonthByButtons = allows;
    [self setNeedsDisplay];
}

-(void)setAllowsChangeMonthBySwipe:(BOOL)allows
{
    _allowsChangeMonthBySwipe = allows;
    _swipeleft.enabled = allows;
    _swipeRight.enabled = allows;
}

-(void)setHideMonthLabel:(BOOL)hideMonthLabel
{
    _hideMonthLabel = hideMonthLabel;
    [self setNeedsDisplay];
}

-(void)setSelectedDate:(NSDate *)selectedDate
{
    _selectedDate = selectedDate;
    [self setNeedsDisplay];
}


#pragma mark - Public methods

-(void)showNextMonth{
    
    _currentShowComps.day = 1;
    _currentShowComps.month ++;
    NSDate * nextMonthDate =[_calendar dateFromComponents:_currentShowComps];
    
    if ([self canSwipeToDate:nextMonthDate]){
        [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
        
        if (!_keepSelDayWhenMonthChange){
            _selectedDate = [_calendar dateFromComponents:_currentShowComps];
        }
        [self performViewAnimation:_nextMonthAnimation];
    }else{
        _currentShowComps.month --;
        [self performViewNoSwipeAnimation];
    }
}


-(void)showPreviousMonth{
    _currentShowComps.month --;
    
    NSDate * prevMonthDate = [_calendar dateFromComponents:_currentShowComps];
    NSRange previousMonthDays = [_calendar rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:prevMonthDate];
    
    _currentShowComps.day = previousMonthDays.length;
    
    prevMonthDate = [_calendar dateFromComponents:_currentShowComps];
    
    if ([self canSwipeToDate:prevMonthDate]){
        [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
        if (!_keepSelDayWhenMonthChange){
            _selectedDate = [_calendar dateFromComponents:_currentShowComps];
        }
        [self performViewAnimation:_prevMonthAnimation];
    }else{
        _currentShowComps.month++;
        [self performViewNoSwipeAnimation];
    }
}

#pragma mark - Various methods

-(BOOL)canSwipeToDate:(NSDate *)date{
    BOOL canSwipeMin = YES,canSwipeMax = YES;
    long long toDateTime = [date timeIntervalSince1970];
    if (self.minDate) {
        NSDate *minZeroDate = [self getZeroDateWithDate:self.minDate];
        long long minDateTime = [minZeroDate timeIntervalSince1970];
        canSwipeMin = toDateTime >= minDateTime;
    }
    if (self.maxDate) {
        NSDate *maxZeroDate = [self getZeroDateWithDate:self.maxDate];
        long long maxDateTime = [maxZeroDate timeIntervalSince1970];
        canSwipeMax = toDateTime <= maxDateTime;
    }
    return canSwipeMin&&canSwipeMax;
}

- (NSDate*)getZeroDateWithDate:(NSDate*)date{
    NSDateComponents *components = [_calendar components:_dayInfoUnits fromDate:date];
    return [_calendar dateFromComponents:components];
}

-(void)performViewAnimation:(UIViewAnimationOptions)animation{
    [UIView transitionWithView:self
                      duration:0.5f
                       options:animation
                    animations:^ { [self setNeedsDisplay]; }
                    completion:nil];
}

-(void)performViewNoSwipeAnimation{
    _shakeDirection = 1;
    _shakes = 0;
    [self shakeView:self];
}

-(void)shakeView:(UIView *)theOneYouWannaShake{
    [UIView animateWithDuration:0.05 animations:^
     {
         theOneYouWannaShake.transform = CGAffineTransformMakeTranslation(5*_shakeDirection, 0);
     } completion:^(BOOL finished) {
         if(_shakes >= 4){
             theOneYouWannaShake.transform = CGAffineTransformIdentity;
             return;
         }
         _shakes++;
         _shakeDirection = _shakeDirection * -1;
         [self shakeView:theOneYouWannaShake];
     }];
}

#pragma mark - Button creation and configuration

-(UIButton *)dayButtonWithFrame:(CGRect)frame{
    UIButton *button  = [UIButton buttonWithType:UIButtonTypeCustom];
    button.titleLabel.font = _defaultFont;
    button.frame = frame;
    button.layer.borderColor = _borderColor.CGColor;
    [button addTarget:self action:@selector(tappedDate:) forControlEvents:UIControlEventTouchUpInside];
    return button;
}

-(void)configureDayButton:(UIButton *)button withDay:(NSInteger)day andComps:(NSDateComponents*)comps{
    [button setTitle:[NSString stringWithFormat:@"%ld",day] forState:UIControlStateNormal];
    button.tag = day;
    [self setupDayButtonSetting:button andIsThisDay:[self isThisDayWithComps:comps andDay:day]];
}

- (void)configurePreOrNextDayButton:(UIButton *)button withDay:(NSInteger)day andComps:(NSDateComponents*)comps{
    [button setTitle:[NSString stringWithFormat:@"%ld",day>100?(day-100):(100-day)] forState:UIControlStateNormal];
    button.tag = day;
    button.alpha = 0.6f;
    [self setupDayButtonSetting:button andIsThisDay:[self isThisDayWithComps:comps andDay:day>100?(day-100):(100-day)]];
}

- (void)setupDayButtonSetting:(UIButton*)button andIsThisDay:(BOOL)isThisDay{
    if(isThisDay){
        // Selected button
        button.layer.borderWidth = 0;
        [button setTitleColor:_dayTxtColorSelected forState:UIControlStateNormal];
        [button setBackgroundColor:_dayBgColorSelected];
    }else{
        // Unselected button
        button.layer.borderWidth = _borderWidth/2.f;
        [button setTitleColor:_dayTxtColorWithoutData forState:UIControlStateNormal];
        [button setBackgroundColor:_dayBgColorWithoutData];
    }

}

- (BOOL)isThisDayWithComps:(NSDateComponents*)comps andDay:(NSInteger)day{
    NSDateComponents *components = [_calendar components:_dayInfoUnits fromDate:[_calendar dateFromComponents:comps]];
    components.day = day;
    NSDate *date = [_calendar dateFromComponents:components];
    NSDate *selectedDate = [_calendar dateFromComponents:_selectedComps];
    if ([date timeIntervalSince1970] == [selectedDate timeIntervalSince1970]) {
        return YES;
    }
    return NO;
}

#pragma mark - Action methods

-(void)tappedDate:(UIButton *)sender{
    
    if (_selectedDate == nil) {
        _selectedDate = [NSDate date];
    }
    NSInteger day = sender.tag;
    if (sender.tag > 50){
        if (sender.tag > 100) {
            [self showPreviousMonth];
        }else{
            [self showNextMonth];
        }
        return;
    }
    if (![self canTapDate:day]) {
        return;
    }
    _selectedComps.day  = day;
    _selectedComps.month = _currentShowComps.month;
    _selectedComps.year = _currentShowComps.year;
    _selectedDate = [_calendar dateFromComponents:_selectedComps];
    [self.subviews makeObjectsPerformSelector:@selector(removeFromSuperview)];
    [self setNeedsDisplay];
    _didSelectedDate = _selectedDate;
}

- (BOOL)canTapDate:(NSInteger)day{
    NSDateComponents *components = [_calendar components:_dayInfoUnits fromDate:[_calendar dateFromComponents:_currentShowComps]];
    components.day = day;
    NSDate *selectedDate = [_calendar dateFromComponents:components];
    BOOL canTap = [self canSwipeToDate:selectedDate];
    if (!canTap) {
        [self performViewNoSwipeAnimation];
    }
    return canTap;
}

#pragma mark - Drawing methods

- (void)drawRect:(CGRect)rect{
    
    _currentShowComps.day = 1;
    NSDate *firstDayOfMonth = [_calendar dateFromComponents:_currentShowComps];
    NSDateComponents *comps = [_calendar components:NSWeekdayCalendarUnit fromDate:firstDayOfMonth];
    
    NSInteger weekdayBeginning = [comps weekday];// Starts at 1 on Sunday
    weekdayBeginning -= 1;
    if(weekdayBeginning < 0){
        weekdayBeginning += 7;// Starts now at 0 on Monday
    }
    
    NSRange days = [_calendar rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:[_calendar dateFromComponents:_currentShowComps]];
    
    NSInteger monthLength = days.length;
    NSInteger remainingDays = (monthLength + weekdayBeginning) % 7;
    
    // Frame drawing
    NSInteger minY = _originY + _dayWidth;
    NSInteger maxY = _originY + _dayWidth * (NSInteger)(1+(monthLength+weekdayBeginning)/7) + ((remainingDays !=0)? _dayWidth:0);
    
    if (_delegate != nil && [_delegate respondsToSelector:@selector(setHeightNeeded:)]){
        [_delegate setHeightNeeded:maxY];
    }
    
    CGColorSpaceRef baseSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGContextSetFillColorWithColor(context, _borderColor.CGColor);
    CGContextAddRect(context, CGRectMake(_originX - _borderWidth/2.f, minY - _borderWidth/2.f, 7*_dayWidth + _borderWidth, _borderWidth));
    CGContextAddRect(context, CGRectMake(_originX - _borderWidth/2.f, maxY - _borderWidth/2.f, 7*_dayWidth + _borderWidth, _borderWidth));
    CGContextAddRect(context, CGRectMake(_originX - _borderWidth/2.f, minY - _borderWidth/2.f, _borderWidth, maxY - minY));
    CGContextAddRect(context, CGRectMake(_originX + 7*_dayWidth - _borderWidth/2.f, minY - _borderWidth/2.f, _borderWidth, maxY - minY));
    CGContextFillPath(context);
    CGColorSpaceRelease(baseSpace), baseSpace = NULL;
    
    BOOL enableNext = YES;
    BOOL enablePrev = YES;
    
    // Previous and next button
    UIButton * buttonPrev = [[UIButton alloc] initWithFrame:CGRectMake(_originX, 0, _dayWidth, _dayWidth)];
    [buttonPrev setTitle:@"<" forState:UIControlStateNormal];
    [buttonPrev setTitleColor:TitleColor forState:UIControlStateNormal];
    [buttonPrev addTarget:self action:@selector(showPreviousMonth) forControlEvents:UIControlEventTouchUpInside];
    buttonPrev.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:16.0f];
//    [self addSubview:buttonPrev];
    
    UIButton * buttonNext = [[UIButton alloc] initWithFrame:CGRectMake(self.bounds.size.width - _dayWidth - _originX, 0, _dayWidth, _dayWidth)];
    [buttonNext setTitle:@">" forState:UIControlStateNormal];
    [buttonNext setTitleColor:TitleColor forState:UIControlStateNormal];
    [buttonNext addTarget:self action:@selector(showNextMonth) forControlEvents:UIControlEventTouchUpInside];
    buttonNext.titleLabel.font = [UIFont fontWithName:@"HelveticaNeue" size:16.0f];
//    [self addSubview:buttonNext];
    
    
    if (!_allowsChangeMonthByButtons)
    {
        buttonNext.hidden = YES;
        buttonPrev.hidden = YES;
    }
    if (_delegate != nil && [_delegate respondsToSelector:@selector(setEnabledForPrevMonthButton:nextMonthButton:)]){
        [_delegate setEnabledForPrevMonthButton:enablePrev nextMonthButton:enableNext];
    }

    // Month label
    NSDateFormatter *format = [[NSDateFormatter alloc] init];
    [format setDateFormat:@"MM-yyyy"];
    NSString *dateString  = [[format stringFromDate:[_calendar dateFromComponents:_currentShowComps]] capitalizedString];
    
    NSArray *dateArray = [dateString componentsSeparatedByString:@"-"];
    
    NSString *myDateString = [NSString stringWithFormat:@"%@年  %@月", dateArray[1], dateArray[0]];
    
    if (!_hideMonthLabel)
    {
        UILabel *titleText = [[UILabel alloc]initWithFrame:CGRectMake(0,0, self.bounds.size.width, _originY)];
        titleText.textAlignment = NSTextAlignmentCenter;
        titleText.text = myDateString;
        titleText.font = _titleFont;
        titleText.textColor = _monthAndDayTextColor;
        [self addSubview:titleText];
    }
    
    if (_delegate != nil && [_delegate respondsToSelector:@selector(setMonthLabel:)]){
        [_delegate setMonthLabel:dateString];
    }
    
    // Day labels
    __block CGRect frameWeekLabel = CGRectMake(0, _originY, _dayWidth, _dayWidth);
    [_weekDayNames  enumerateObjectsUsingBlock:^(NSString * dayOfWeekString, NSUInteger idx, BOOL *stop){
         frameWeekLabel.origin.x = _originX+(_dayWidth*idx);
         UILabel *weekNameLabel = [[UILabel alloc] initWithFrame:frameWeekLabel];
         weekNameLabel.text = dayOfWeekString;
         weekNameLabel.textColor = TitleColor;
         weekNameLabel.font = _weekdayFont;
         weekNameLabel.backgroundColor = [UIColor clearColor];
         weekNameLabel.textAlignment = NSTextAlignmentCenter;
         [self addSubview:weekNameLabel];
     }];
    
    // Current month
    for (NSInteger i= 0; i<monthLength; i++){
        NSInteger offsetX = (_dayWidth*((i+weekdayBeginning)%7));
        NSInteger offsetY = (_dayWidth *((i+weekdayBeginning)/7));
        UIButton *button = [self dayButtonWithFrame:CGRectMake(_originX+offsetX, _originY+_dayWidth+offsetY, _dayWidth, _dayWidth)];
        [self configureDayButton:button withDay:i+1 andComps:_currentShowComps];
        [self addSubview:button];
    }
    
    // Previous month
    NSDateComponents *previousMonthComponents = [_calendar components:_dayInfoUnits fromDate:[_calendar dateFromComponents:_currentShowComps]];
    previousMonthComponents.month --;
    NSDate *previousMonthDate = [_calendar dateFromComponents:previousMonthComponents];
    
    NSRange previousMonthDays = [_calendar rangeOfUnit:NSDayCalendarUnit inUnit:NSMonthCalendarUnit forDate:previousMonthDate];
    NSInteger maxDate = previousMonthDays.length - weekdayBeginning;
    for (int i=0; i<weekdayBeginning; i++) {
        previousMonthComponents.day     = maxDate+i+1;
        NSInteger offsetX               = (_dayWidth*(i%7));
        NSInteger offsetY               = (_dayWidth *(i/7));
        UIButton *button                = [self dayButtonWithFrame:CGRectMake(_originX+offsetX, _originY + _dayWidth + offsetY, _dayWidth, _dayWidth)];

        [self configurePreOrNextDayButton:button withDay:(100+previousMonthComponents.day) andComps:previousMonthComponents];
        [self addSubview:button];
    }
    previousMonthComponents.day = previousMonthDays.length;
    NSDate *prevMonthDate =[_calendar dateFromComponents:previousMonthComponents];
    if (![self canSwipeToDate:prevMonthDate])
    {
        buttonPrev.alpha = 0.5f;
        buttonPrev.enabled = NO;
        enablePrev = NO;
    }
    
    // Next month
    if(remainingDays == 0)
        return ;
    
    NSDateComponents *nextMonthComponents = [_calendar components:_dayInfoUnits fromDate:[_calendar dateFromComponents:_currentShowComps]];
    nextMonthComponents.month ++;
    
    for (NSInteger i=remainingDays; i<7; i++)
    {
        nextMonthComponents.day         = (i+1)-remainingDays;
        NSInteger offsetX               = (_dayWidth*((i) %7));
        NSInteger offsetY               = (_dayWidth *((monthLength+weekdayBeginning)/7));
        UIButton *button                = [self dayButtonWithFrame:CGRectMake(_originX+offsetX, _originY + _dayWidth + offsetY, _dayWidth, _dayWidth)];

        [self configurePreOrNextDayButton:button withDay:(100-nextMonthComponents.day) andComps:nextMonthComponents];
        [self addSubview:button];
    }
    nextMonthComponents.day = 1;
    NSDate *nextMonthDate =[_calendar dateFromComponents:nextMonthComponents];
    if (![self canSwipeToDate:nextMonthDate])
    {
        buttonNext.alpha = 0.5f;
        buttonNext.enabled = NO;
        enableNext = NO;
    }
}

+ (instancetype)createCalendarViewWithDidSelectedDate:(NSDate *)didSelectedDate minDate:(NSDate *)minDate maxDate:(NSDate *)maxDate andFrame:(CGRect)frame{
    return [[CalendarView alloc]initCalendarViewWithDidSelectedDate:didSelectedDate minDate:minDate maxDate:maxDate andFrame:frame];
}

- (void)showSelectedDay{
    
}
@end
